//////////////////////////////////////////////////////////////////////
//
//  CryEngine Source code
//	
//	File:Anim_Weights.cpp
//  Implementation of Animation class for parameterisation
//
//	History:
//	January 12, 2005: Created by Ivo Herzeg <ivo@crytek.de>
//
//////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include <I3DEngine.h>
#include <IRenderAuxGeom.h>
#include "CharacterInstance.h"
#include "Model.h"
#include "ModelSkeleton.h"
#include "CharacterManager.h"
#include <float.h>
#include "LMG.h"
#include "LoaderDBA.h"

CAnimation& CSkeletonAnim::GetAnimFromFIFO(uint32 nLayer, uint32 num )
{








	uint32 numAnimsInFifo = m_layers[nLayer].m_transitionQueue.m_animations.size();
	if (num>=numAnimsInFifo)
		return g_DefaultAnim;
	return m_layers[nLayer].m_transitionQueue.m_animations[num];

}

int CSkeletonAnim::GetNumAnimsInFIFO(uint32 nLayer) const
{
	return m_layers[nLayer].m_transitionQueue.m_animations.size();
}

int CSkeletonAnim::GetNumAnimsInFIFOAndDeferredQueue(uint32 nLayer)
{
	int iNumAnimsInFIFO = GetNumAnimsInFIFO(nLayer);

	int iNumAnimsInDeferredQueue = 0;
	const int iDeferredQueueSize = m_arrDeferredQueueUpdates.size();
	for (int i = 0; i < iDeferredQueueSize; ++i)
	{
		if (m_arrDeferredQueueUpdates[i].m_nLayer == nLayer)
			++iNumAnimsInDeferredQueue;
	}

	return iNumAnimsInFIFO + iNumAnimsInDeferredQueue;
}

CAnimation& CSkeletonAnim::GetAnimFromFIFOAndDeferredQueue(uint32 nLayer, uint32 num)
{
	int iNumAnimsInFIFO = GetNumAnimsInFIFO(nLayer);
	if ((int)num >= iNumAnimsInFIFO)
	{
		const int iIdx = num - iNumAnimsInFIFO;
		int iLayerAnimCount = 0;

		const int iDeferredQueueSize = m_arrDeferredQueueUpdates.size();
		for (int i = 0; i < iDeferredQueueSize; ++i)
		{
			DeferredQueueUpdate& deferredAnim = m_arrDeferredQueueUpdates[i];
			if (deferredAnim.m_nLayer == nLayer)
			{
				if (iLayerAnimCount == iIdx)
					return deferredAnim.m_Anim;

				++iLayerAnimCount;
			}
		}

		return g_DefaultAnim;
	}
	else
	{
		return GetAnimFromFIFO(nLayer, num);
	}
}

// makes sure there's no anim in this layer's queue that could cause a delay (useful when called just before 
// start playing an animation that you want to be 100% sure is going to be transitioned to immediately)
SPU_NO_INLINE void CSkeletonAnim::RemoveTransitionDelayConditions(uint32 nLayer)
{
	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;
	const uint32 numAnimsInQueue = m_layers[nLayer].m_transitionQueue.m_animations.size();

	// search for delay conditions and get rid of them
	uint32 nMaxActiveInQueue = min(MAX_EXEC_QUEUE, numAnimsInQueue);
	for (uint32 i = 0; i < nMaxActiveInQueue; ++i)
	{
		const CAnimation &anim = m_layers[nLayer].m_transitionQueue.m_animations[i];

		// is anim in memory?
		bool bNotInMemory = false;
		if (!anim.m_Parametric)
		{
			int32 nAnimID = anim.m_nAnimID;
			const ModelAnimationHeader* pAnim = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(nAnimID) );
			if (pAnim->m_nAssetType == CAF_File)
			{
				int32 nGlobalID = pAnim->m_nGlobalAnimId;
				GlobalAnimationHeaderCAF& rGAHeader = g_AnimationManager.m_arrGlobalCAF[nGlobalID];
				bNotInMemory = !rGAHeader.IsAssetLoaded();
			}
			else if (pAnim->m_nAssetType == AIM_File)
			{
				int32 nGlobalID = pAnim->m_nGlobalAnimId;
				GlobalAnimationHeaderAIM& rGAHeader = g_AnimationManager.m_arrGlobalAIM[nGlobalID];
				bNotInMemory = rGAHeader.IsAssetOnDemand() && !rGAHeader.IsAssetLoaded();
			}
		}
		else
		{
			const uint32 num = anim.m_Parametric->m_numAnims;
			for (uint32 a = 0; (a < num) && !bNotInMemory; ++a)
			{
				const int32 nAnimID = anim.m_Parametric->m_nAnimID[a];
				const ModelAnimationHeader* pAnim = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(nAnimID) );
				if (pAnim->m_nAssetType == CAF_File)
				{
					int32 nGlobalID = pAnim->m_nGlobalAnimId;
					GlobalAnimationHeaderCAF& rGAHeader = g_AnimationManager.m_arrGlobalCAF[nGlobalID];
					bNotInMemory = !rGAHeader.IsAssetLoaded();
				}
				if (pAnim->m_nAssetType == AIM_File) //--- Not needed here?
				{
					int32 nGlobalID = pAnim->m_nGlobalAnimId;
					GlobalAnimationHeaderAIM& rGAHeader = g_AnimationManager.m_arrGlobalAIM[nGlobalID];
					bNotInMemory = rGAHeader.IsAssetOnDemand() && !rGAHeader.IsAssetLoaded();
				}
			}
		}

		if (!bNotInMemory)
		{
			// Remove flags that could delay the transition (must be in synch with CSkeletonAnim::AppendAnimationToQueue and 
			// CSkeletonAnim::EvaluateTransitionFlags)
			m_layers[nLayer].m_transitionQueue.m_animations[i].m_AnimParams.m_nFlags &= ~(CA_START_AT_KEYTIME | CA_START_AFTER | CA_IDLE2MOVE | CA_MOVE2IDLE);

			// and mark it as activated
			m_layers[nLayer].m_transitionQueue.m_animations[i].m_bActivated = 1;
		}
		else
		{
			// Just get rid of it if not in memory
			if (RemoveAnimFromFIFO(nLayer, i))
			{
				--i;
				--nMaxActiveInQueue;
			}
		}
	}
}

SPU_NO_INLINE bool CSkeletonAnim::StartAnimation(const char* szAnimName, const struct CryCharAnimationParams& Params)
{
	if (szAnimName==0)
	{
#if !defined(__SPU__)
		g_pILog->LogError ("No name for animation specified");
#endif
		assert(!"No name for animation specified");
		return false;
	}

	if (szAnimName)
	{
		uint32 isNullAnimation = strstr(szAnimName, "null") || strstr(szAnimName, "NULL");
		if (isNullAnimation)
		{
			m_bSetDefaultPose=1;
			return false; 
		}
	}

	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;
	int32 nAnimID = pAnimationSet->GetAnimIDByName(szAnimName);
	if (nAnimID<0)	
	{
		if (Console::GetInst().ca_AnimWarningLevel > 0)
		{
			bool bShowWarnings = !(Params.m_nFlags & CA_SUPPRESS_WARNINGS);
			if (bShowWarnings)
				AnimFileWarning(m_pInstance->m_pModel->GetModelFilePath(),"animation-name '%s' not in CAL-file", szAnimName );
			//CRY_ASSERT_MESSAGE(!"animation-name not in CAL-file",szAnimName);
		}
		return 0;
	}

	return StartAnimationById(nAnimID, Params);
}

SPU_NO_INLINE bool CSkeletonAnim::StartAnimationById(int32 id, const struct CryCharAnimationParams& Params)
{
	ANIMATION_LIGHT_PROFILER();

	DEFINE_PROFILER_FUNCTION();
	if (m_pInstance->m_bEnableStartAnimation==0)
		return 0;

	if (g_pCharacterManager->m_AllowStartOfAnimation==0)
		return 0;

	if (Params.m_nLayerID>=numVIRTUALLAYERS || Params.m_nLayerID<0)
		return 0;

	if(!m_pSkeletonPose->m_parrModelJoints)
		return 0;

	const char* RootName = m_pSkeletonPose->m_parrModelJoints[0].GetJointName();
	if (RootName == 0)
		return 0;

	m_bSetDefaultPose=0;

	CryCharAnimationParams AnimPrams = Params;
	if (AnimPrams.m_nLayerID>0)
	{
		//	AnimPrams.m_nFlags|=CA_PARTIAL_BODY_UPDATE;
		uint32 loop=AnimPrams.m_nFlags&CA_LOOP_ANIMATION;
		uint32 repeat=AnimPrams.m_nFlags&CA_REPEAT_LAST_KEY;
		if (loop==0 && repeat==0)
		{
			AnimPrams.m_nFlags|=CA_REPEAT_LAST_KEY;
			AnimPrams.m_nFlags|=CA_FADEOUT;
		}
	}

	//----------------------------------------------------------------------------

	uint32 nDisableMultilayer = AnimPrams.m_nFlags & CA_DISABLE_MULTILAYER;
	if (nDisableMultilayer)
	{
		AnimPrams.m_fAllowMultilayerAnim=0.0f;
	}

	//----------------------------------------------------------------------------

	bool bShowWarnings = !(Params.m_nFlags & CA_SUPPRESS_WARNINGS);
	uint32 TrackViewExclusive = Params.m_nFlags & CA_TRACK_VIEW_EXCLUSIVE;
	if (m_TrackViewExclusive && !TrackViewExclusive)
		return 0;

	const ModelAnimationHeader* pAnim=SPU_CONST_MAIN_PTR(0);
	int32 nAnimID			= id;

	const ModelAnimationHeader* pAnimAim0=SPU_CONST_MAIN_PTR(0); 
	int32 nAnimAimID0		=-1;
	const ModelAnimationHeader* pAnimAim1=SPU_CONST_MAIN_PTR(0); 
	int32 nAnimAimID1		=-1;

	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;

	assert(Params.m_fTransTime<60.0f); //transition times longer than 1 minute are useless

	if (nAnimID<0)	
		return 0;

	//--------------------------------------------------------------
	//---                evaluate the 1st animation              ---
	//--------------------------------------------------------------

	pAnim = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(nAnimID) );

	if (pAnim==0)	
	{
		assert(0);
		return 0;
	}

	const char* szAnimName = pAnim->GetAnimName();

	uint32 nGlobalID = pAnim->m_nGlobalAnimId;


	//-----------------------------------------------------------------------------------------------------------------------
	//--- CAF start     -----------------------------------------------------------------------------------------------------
	//-----------------------------------------------------------------------------------------------------------------------
	if (pAnim->m_nAssetType==CAF_File)
	{
		GlobalAnimationHeaderCAF& rCAF = g_AnimationManager.m_arrGlobalCAF[nGlobalID];

		uint32 IsCreated = rCAF.IsAssetCreated();
		if (IsCreated==0)		
		{
			if (Console::GetInst().ca_AnimWarningLevel > 0)
				AnimFileWarning(m_pInstance->m_pModel->GetModelFilePath(),"Asset for animation-name '%s' does not exist", pAnim->GetAnimName());
			return 0;
		}

	//	g_pISystem->Warning( VALIDATOR_MODULE_ANIMATION,VALIDATOR_WARNING,	VALIDATOR_FLAG_FILE,rCAF.GetFilePath(),	"Animation started: loaded: %d",rCAF.IsAssetLoaded() );

		if (rCAF.IsAssetLoaded()==0 && rCAF.IsAssetRequested()==0)
			rCAF.StartStreamingCAF();
	}


	//-----------------------------------------------------------------------------------------------------------------------
	//--- CAF start     -----------------------------------------------------------------------------------------------------
	//-----------------------------------------------------------------------------------------------------------------------
	if (pAnim->m_nAssetType==AIM_File)
	{
		GlobalAnimationHeaderAIM& rAIM = g_AnimationManager.m_arrGlobalAIM[nGlobalID];
		uint32 IsCreated = rAIM.IsAssetCreated();
		if (IsCreated==0)		
		{
			if (Console::GetInst().ca_AnimWarningLevel > 0)
				AnimFileWarning(m_pInstance->m_pModel->GetModelFilePath(),"Asset for animation-name '%s' does not exist", pAnim->GetAnimName());
			return 0;
		}

		if (rAIM.IsAimposeUnloaded())
		{
			uint32 nLayerAim =m_pSkeletonPose->GetPoseBlenderAim().m_blender.m_nDirLayer;
			uint32 nLayerLook=m_pSkeletonPose->GetPoseBlenderLook().m_blender.m_nDirLayer;
			if ((Params.m_nLayerID != nLayerAim) && (Params.m_nLayerID != nLayerLook))
			{
				if (Console::GetInst().ca_AnimWarningLevel>0 && bShowWarnings)
					AnimFileWarning(m_pInstance->m_pModel->GetModelFilePath(),"trying to play an unloaded aim-pose or look-pose '%s'", szAnimName );
				return 0;
			}
		}
	}


	//-----------------------------------------------------------------------------------------------------------------------
	//--- LMG start     -----------------------------------------------------------------------------------------------------
	//-----------------------------------------------------------------------------------------------------------------------
	if (pAnim->m_nAssetType==LMG_File)
	{
		GlobalAnimationHeaderLMG& rLMG = g_AnimationManager.m_arrGlobalLMG[nGlobalID];
		assert( rLMG.IsAssetLMG() ); 
		uint32 IsCreated = rLMG.IsAssetCreated();
		if (IsCreated==0)		
		{
			if (Console::GetInst().ca_AnimWarningLevel>0)
				AnimFileWarning(m_pInstance->m_pModel->GetModelFilePath(),"Asset for animation-name '%s' does not exist", pAnim->GetAnimName());
			return 0;
		}

		if (Params.m_nLayerID)
			return 0; //LMGs in laers are currently not supported

		if (rLMG.IsAssetLMGValid()==0)
		{
			ANIM_ASSET_CHECK_TRACE(rLMG.IsAssetLMGValid() != 0, ("LMG is invalid: '%s'", szAnimName));
			return 0;
		} 


		uint32 numLMGAnims = rLMG.m_arrBSAnimations.size();
		for (uint32 i=0; i<numLMGAnims; i++)
		{
			int local = pAnimationSet->GetAnimIDByCRC( rLMG.m_arrBSAnimations[i].m_animName.m_CRC32 );
			int global = pAnimationSet->GetGlobalIDByAnimID(local);
			if (global != -1)
			{
				GlobalAnimationHeaderCAF& rGAH = g_AnimationManager.m_arrGlobalCAF[global];
				if (rGAH.IsAssetLoaded()==0 && rGAH.IsAssetRequested()==0)
					rGAH.StartStreamingCAF();
			}
		}

		uint32 numSpliceAnims = rLMG.m_strSpliceAnim.size();
		for (uint32 i=0; i<numSpliceAnims; i++)
		{
			int local = pAnimationSet->GetAnimIDByCRC( rLMG.m_strSpliceAnim[i].m_CRC32 );
			int global = pAnimationSet->GetGlobalIDByAnimID(local);
			if (global != -1)
			{
				GlobalAnimationHeaderCAF& rGAH = g_AnimationManager.m_arrGlobalCAF[global];
				if (rGAH.IsAssetLoaded()==0 && rGAH.IsAssetRequested()==0)
					rGAH.StartStreamingCAF();
			}
		}
	}


	//--------------------------------------------------------------
	//---                evaluate the aim-pose                   ---
	//--------------------------------------------------------------
	const char* szAim0 = pAnim->m_strAimPose1[0];
	if (szAim0 && szAim0[0]==0)
		szAim0=0;

	const char* szAim1 = 0;
	if (szAim0)
	{
		nAnimAimID0 = pAnimationSet->GetAnimIDByName( szAim0 );
		if (nAnimAimID0<0)	
		{
			if (Console::GetInst().ca_AnimWarningLevel>0 && bShowWarnings)
				AnimFileWarning(m_pInstance->m_pModel->GetModelFilePath(),"animation-name '%s' not in CAL-file", szAim0);
			assert(0);
			return 0;
		}
		//check if the asset exists
		pAnimAim0 = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(nAnimAimID0) );
		if (pAnimAim0==0)	
		{
			assert(0);	return 0;
		}

		szAim1 = pAnim->m_strAimPose1[1];
		if (szAim1 && szAim1[0]==0) szAim1=0;
		if (szAim1)
		{
			nAnimAimID1 = pAnimationSet->GetAnimIDByName( szAim1 );
			if (nAnimAimID1<0)	
			{
				if (Console::GetInst().ca_AnimWarningLevel>0 && bShowWarnings)
					AnimFileWarning(m_pInstance->m_pModel->GetModelFilePath(),"animation-name '%s' not in CAL-file", szAim1);
				assert(0);
				return 0;
			}
			//check if the asset exists
			pAnimAim1 = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(nAnimAimID1) );
			if (pAnimAim1==0)	
			{
				assert(0);	return 0;
			}

		}
	}




#if !defined(__SPU__) // superimposed is only used from the editor, so no need for it on spu
	if (m_pSkeletonPose->m_Superimposed)
	{
		uint32 tr=m_AnimationDrivenMotion;
		m_AnimationDrivenMotion=0;
		extern std::vector< std::vector<DebugJoint> > g_arrSkeletons;
		extern int32 g_nAnimID;
		extern int32 g_nGlobalAnimID;
		g_nAnimID				=	nAnimID;
		g_nGlobalAnimID	=	nGlobalID;
		m_AnimationDrivenMotion=tr;
	}
#endif

	bool result = AnimationToQueue ( pAnim,nAnimID, pAnimAim0,nAnimAimID0,pAnimAim1,nAnimAimID1,  -1, AnimPrams) > 0;

	if (result)
	{
		if (TrackViewExclusive)
		{
			int32 Layer = Params.m_nLayerID;
			int32 numAnims = m_layers[Layer].m_transitionQueue.m_animations.size()-1;
			if (numAnims>0)
				for (int32 i=0; i<numAnims; i++)
					m_layers[Layer].m_transitionQueue.m_animations[i].m_AnimParams.m_fTransTime=fabsf(m_layers[Layer].m_transitionQueue.m_animations[i].m_AnimParams.m_fTransTime*0.01f);
		}

		m_layers[Params.m_nLayerID].m_transitionQueue.m_blendingTime = fabsf(Params.m_fTransTime);
		m_layers[Params.m_nLayerID].m_transitionQueue.m_blendingMultiplier = Params.m_fLayerWeight;
	}

	return result;
}




// stops the animation at the given layer, and returns true if the animation was
// actually stopped (if the layer existed and the animation was played there)
bool CSkeletonAnim::StopAnimationInLayer (int32 nLayer, f32 BlendOutTime)
{
	if (nLayer<0 || nLayer>=numVIRTUALLAYERS)
	{
		AnimFileWarning(m_pInstance->m_pModel->GetModelFilePath(),"illegal layer index used in function StopAnimationInLayer: '%d'", nLayer);
		assert(0);
		return 0;
	}

	//	const char* mname = m_pInstance->GetFilePath();
	//	if ( strcmp(mname,"objects/library/architecture/multiplayer/prototype_factory/pf_factory_gate.cga")==0 )
	//		g_pISystem->Warning( VALIDATOR_MODULE_ANIMATION,VALIDATOR_WARNING, VALIDATOR_FLAG_FILE,mname,	"StopAnimationInLayer");
	m_pInstance->m_nForceUpdate=1;
	SetActiveLayer(nLayer,0);
	m_layers[nLayer].m_transitionQueue.m_blendingTime=BlendOutTime;
	if (nLayer)
		return 1;

	ClearFIFOLayer(nLayer);
	return 1;
}

// stops the animation at the given layer, and returns true if the animation was
// actually stopped (if the layer existed and the animation was played there)
bool CSkeletonAnim::StopAnimationsAllLayers ()
{
	//	const char* mname = m_pInstance->GetFilePath();
	//	if ( strcmp(mname,"objects/library/architecture/multiplayer/prototype_factory/pf_factory_gate.cga")==0 )
	//		g_pISystem->Warning( VALIDATOR_MODULE_ANIMATION,VALIDATOR_WARNING, VALIDATOR_FLAG_FILE,mname,	"StopAnimationsAllLayers");

	m_pInstance->m_nForceUpdate=1;
	for (uint32 i=0; i<numVIRTUALLAYERS; i++)
		ClearFIFOLayer(i);

	return 1;
}

bool CSkeletonAnim::RemoveAnimFromFIFO(uint32 nLayer, uint32 num, bool forceRemove)
{

	if (nLayer >= numVIRTUALLAYERS)
		return false;
	DynArray<CAnimation>& rAnimations = m_layers[nLayer].m_transitionQueue.m_animations;
	if (num >= (uint32)rAnimations.size())
		return false;
	//If not forcing the removal, disallow the removal of active animations
	if (!forceRemove && rAnimations[num].m_bActivated)
		return false;

	UnloadAnimationAssets(rAnimations, num);

	rAnimations.erase(rAnimations.begin() + num);

	//If there are no more animations in the transition queue, then make sure to inactivate the layer for blending.
	if(rAnimations.empty())
	{
		SetActiveLayer(nLayer, 0);
	}

	return true;
}



//////////////////////////////////////////////////////////////////////////
// push a new animation into the queue
//////////////////////////////////////////////////////////////////////////
uint32 CSkeletonAnim::AnimationToQueue ( const ModelAnimationHeader* pAnim0,int nID0,     const ModelAnimationHeader* pAnimAim0,int nAimID0,const ModelAnimationHeader* pAnimAim1,int nAimID1,   f32 btime, const CryCharAnimationParams& AnimParams)
{
	const uint32 numAnimsInQueue = m_layers[AnimParams.m_nLayerID].m_transitionQueue.m_animations.size();
	if (numAnimsInQueue>0x10)
	{
		AnimFileWarning(m_pInstance->m_pModel->GetModelFilePath(),"Animation-queue overflow. More than %d entries. This is a serious performance problem.",numAnimsInQueue);
		return 0;
	}

	m_pInstance->m_nForceUpdate=1;
	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;
	uint32 status=0;

	// use for multi threading to remember if we have to remove the first
	// animation from the queue in case StartAnimations was called while AnimationComputations were
	// running(for example SPUs)
	bool bRemoveFirstAnimInQueue = false;

	if (pAnim0)
	{
		uint32 nGlobalAnimId = pAnim0->m_nGlobalAnimId;
		if (pAnim0->m_nAssetType==LMG_File)
		{
			GlobalAnimationHeaderLMG& rLMG = g_AnimationManager.m_arrGlobalLMG[nGlobalAnimId];
			if (rLMG.IsAssetLMG())
				status|=1;
			rLMG.m_nTouchedCounter++;
		}

	}

	uint32 aar=AnimParams.m_nFlags&CA_ALLOW_ANIM_RESTART;
	if (aar==0)
	{
		//don't allow to start the same animation twice
		uint32 numAnimsOnStack = m_layers[AnimParams.m_nLayerID].m_transitionQueue.m_animations.size();
		if (numAnimsOnStack)
		{

			//check is it the same animation only if the aim poses are different
			int32 nPrevAimID0 = m_layers[AnimParams.m_nLayerID].m_transitionQueue.m_animations[numAnimsOnStack-1].m_nAnimDirID0;
			if (nPrevAimID0<0) nPrevAimID0=-1;

			if (nPrevAimID0==nAimID0)
			{
				CAnimation const & lastAnimation = m_layers[AnimParams.m_nLayerID].m_transitionQueue.m_animations[numAnimsOnStack-1];

				int32 id0=lastAnimation.m_nAnimID;
				if (id0<0) id0=-1;

				int32 num=(id0>=0); 

				if (num==1)
					if (lastAnimation.m_nAnimID==nID0)
						return 0;
			}
		}
	}


	if (AnimParams.m_nFlags&CA_REMOVE_FROM_FIFO)
	{

		if( m_bDidRun )
		{
			bRemoveFirstAnimInQueue = true;
		}
		else if(GetNumAnimsInFIFO(AnimParams.m_nLayerID) >= 15)
		{
			RemoveAnimFromFIFO(AnimParams.m_nLayerID, 0, true);
		}					
	}


	CAnimation AnimOnStack;


	if (pAnim0)
	{
		AnimOnStack.m_nAnimID = nID0;
		if (pAnim0->m_nAssetType==LMG_File)
		{
			AnimOnStack.m_Parametric = LMG::BuildRuntimeStructLMG(pAnimationSet, pAnim0,nID0);
			//--- Paranoia exit, empty pool
			if (!AnimOnStack.m_Parametric)
				return 0;
		}

		if (pAnimAim0)
		{
			AnimOnStack.m_nAnimDirID0			= nAimID0;
			if (pAnimAim1)
				AnimOnStack.m_nAnimDirID1			= nAimID1;
		}

	}


	AnimOnStack.m_nSegHighest=0;

	if (AnimOnStack.m_Parametric == NULL)
	{
		const ModelAnimationHeader* pAnim = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(nID0) );
		if (pAnim->m_nAssetType==CAF_File)
		{
			int32 GlobalID = pAnimationSet->GetGlobalIDByAnimID_Fast(nID0);
			GlobalAnimationHeaderCAF& rCAF = g_AnimationManager.m_arrGlobalCAF[ GlobalID ];
			rCAF.m_nTouchedCounter++;
			rCAF.m_nRef_at_Runtime++;
			AnimOnStack.m_nSegHighest = rCAF.m_Segments;

			if (!rCAF.IsAssetOnDemand() && rCAF.IsAssetLoaded())
			{
				//The DBA is in memory. Just reset the DBA counter
				size_t numDBA_Files = g_AnimationManager.m_arrGlobalHeaderDBA.size();
				for (uint32 d=0; d<numDBA_Files; d++)
				{
					CGlobalHeaderDBA& pGlobalHeaderDBA = g_AnimationManager.m_arrGlobalHeaderDBA[d];
					if (rCAF.m_FilePathDBACRC32!=pGlobalHeaderDBA.m_FilePathDBACRC32)
						continue;
					if (pGlobalHeaderDBA.m_pDatabaseInfo)
						pGlobalHeaderDBA.m_nTCount=0;
				}
			}
		}
	}
	else
	{
		// AnimParams is not initialized in the constructor anymore.
		// BuildRealTimeLMG does NOT initialize AnimParams implicitly, for cases where the caller overwrite stuff anyway. 
		AnimOnStack.m_Parametric->m_BlendSpace.Init();

		const uint32 numAnims0 = AnimOnStack.m_Parametric->m_numAnims;

		for (uint32 i=0; i<numAnims0; i++)
		{
			int32 nAnimID = AnimOnStack.m_Parametric->m_nAnimID[i];
			const ModelAnimationHeader* pAnim = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(nAnimID) );
			if (pAnim->m_nAssetType==CAF_File)
			{
				int32 GlobalID = pAnimationSet->GetGlobalIDByAnimID_Fast(AnimOnStack.m_Parametric->m_nAnimID[i]);
				GlobalAnimationHeaderCAF& rCAF = g_AnimationManager.m_arrGlobalCAF[ GlobalID ];
				rCAF.m_nTouchedCounter++;
				rCAF.m_nRef_at_Runtime++;

				int32 seg = rCAF.m_Segments;
				if (AnimOnStack.m_nSegHighest<seg)
					AnimOnStack.m_nSegHighest=seg;

				if (!rCAF.IsAssetOnDemand() && rCAF.IsAssetLoaded())
				{
					//The DBA is in memory. Just reset the DBA counter
					size_t numDBA_Files = g_AnimationManager.m_arrGlobalHeaderDBA.size();
					for (uint32 d=0; d<numDBA_Files; d++)
					{
						CGlobalHeaderDBA& pGlobalHeaderDBA = g_AnimationManager.m_arrGlobalHeaderDBA[d];
						if (rCAF.m_FilePathDBACRC32!=pGlobalHeaderDBA.m_FilePathDBACRC32)
							continue;
						if (pGlobalHeaderDBA.m_pDatabaseInfo)
							pGlobalHeaderDBA.m_nTCount=0;
					}
				}

			}
		}

		UpdateMotionParamDescs(*AnimOnStack.m_Parametric, 1.0f);
	}


	if (AnimParams.m_fKeyTime >= 0.0f)
	{
		assert(AnimParams.m_fKeyTime<=1.0f);
		AnimOnStack.m_fAnimTimePrev				=	AnimParams.m_fKeyTime;
		AnimOnStack.m_fAnimTime						=	AnimParams.m_fKeyTime;
	}
	else
	{
		AnimOnStack.m_fAnimTimePrev				=	0.0f;
		AnimOnStack.m_fAnimTime						=	0.0f;
	}

	AnimOnStack.m_fTransitionPriority	=	0.0f;
	AnimOnStack.m_fTransitionWeight   =	0.0f;
	AnimOnStack.m_nRepeatCount        = 0;
	AnimOnStack.m_AnimParams					=	AnimParams;



	assert(AnimOnStack.m_fAnimTime>=0.0f);
	assert(AnimOnStack.m_fAnimTime<=1.0f);

	if( m_bDidRun )
	{
		m_arrDeferredQueueUpdates.push_back( DeferredQueueUpdate( bRemoveFirstAnimInQueue,AnimParams.m_nLayerID, AnimOnStack ));
	}
	else
	{
		AppendAnimationToQueue( AnimParams.m_nLayerID, AnimOnStack );	
	}	

	return 1;
}

void CSkeletonAnim::AppendAnimationToQueue( int32 nLayer, const CAnimation &rAnim )
{
	SetActiveLayer(nLayer,1);

	if (!(rAnim.m_AnimParams.m_nFlags & CA_FORCE_TRANSITION_TO_ANIM))
	{
		CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;
		const uint32 numAnimsInQueue = m_layers[nLayer].m_transitionQueue.m_animations.size();

		m_layers[nLayer].m_transitionQueue.m_animations.push_back( rAnim );

		//activate assets immediately, if possible
		uint32 nMaxActiveInQueue = min(MAX_EXEC_QUEUE, numAnimsInQueue);
		for (uint32 i=0; i<nMaxActiveInQueue; i++)
		{
			m_layers[nLayer].m_transitionQueue.m_animations[i].m_bActivated=0;
			uint32 NotInMemory=0;

			const CAnimation &anim = m_layers[nLayer].m_transitionQueue.m_animations[i];

			if (anim.m_Parametric == NULL)
			{
				int32 nAnimID = anim.m_nAnimID;
				const ModelAnimationHeader* pAnim = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(nAnimID) );
				if (pAnim->m_nAssetType==CAF_File)
				{
					int32 nGlobalID = pAnim->m_nGlobalAnimId;
					GlobalAnimationHeaderCAF& rGAHeader = g_AnimationManager.m_arrGlobalCAF[nGlobalID];
					NotInMemory = !rGAHeader.IsAssetLoaded();
				}
				else if (pAnim->m_nAssetType==AIM_File)
				{
					int32 nGlobalID = pAnim->m_nGlobalAnimId;
					GlobalAnimationHeaderAIM& rGAHeader = g_AnimationManager.m_arrGlobalAIM[nGlobalID];
					NotInMemory = rGAHeader.IsAssetOnDemand() && !rGAHeader.IsAssetLoaded();
				}
			}
			else
			{
				const uint32 num = anim.m_Parametric->m_numAnims;
				for (uint32 a=0; (a<num) && !NotInMemory; a++)
				{
					const int32 nAnimID = anim.m_Parametric->m_nAnimID[a];
					const ModelAnimationHeader* pAnim = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(nAnimID) );
					if (pAnim->m_nAssetType==CAF_File)
					{
						int32 nGlobalID = pAnim->m_nGlobalAnimId;
						GlobalAnimationHeaderCAF& rGAHeader = g_AnimationManager.m_arrGlobalCAF[nGlobalID];
						NotInMemory = !rGAHeader.IsAssetLoaded();
					}
					if (pAnim->m_nAssetType==AIM_File) //--- Not needed here?
					{
						int32 nGlobalID = pAnim->m_nGlobalAnimId;
						GlobalAnimationHeaderAIM& rGAHeader = g_AnimationManager.m_arrGlobalAIM[nGlobalID];
						NotInMemory=rGAHeader.IsAssetOnDemand() && !rGAHeader.IsAssetLoaded();
					}
				}
			}

			m_layers[nLayer].m_transitionQueue.m_animations[i].m_bActivated = (NotInMemory==0 && i==0);

			if (i==0)
				continue;
			if (m_layers[nLayer].m_transitionQueue.m_animations[i-1].m_bActivated==0)
				break;

			uint32 StartAtKeytime = m_layers[nLayer].m_transitionQueue.m_animations[i].m_AnimParams.m_nFlags & CA_START_AT_KEYTIME;
			uint32 StartAfter			= m_layers[nLayer].m_transitionQueue.m_animations[i].m_AnimParams.m_nFlags & CA_START_AFTER;
			uint32 Idle2Move			= m_layers[nLayer].m_transitionQueue.m_animations[i].m_AnimParams.m_nFlags & CA_IDLE2MOVE;
			uint32 Move2Idle			= m_layers[nLayer].m_transitionQueue.m_animations[i].m_AnimParams.m_nFlags & CA_MOVE2IDLE;
			if ( (StartAtKeytime+StartAfter+Idle2Move+Move2Idle+NotInMemory) == 0)
				m_layers[nLayer].m_transitionQueue.m_animations[i].m_bActivated = 1;
		}
	}
	else
	{
		RemoveTransitionDelayConditions(nLayer);
		m_layers[nLayer].m_transitionQueue.m_animations.push_back( rAnim );
	}
}

void CSkeletonAnim::ClearFIFOLayer( uint32 nLayer )
{
#if !defined(__SPU__)
	// make sure no parallel computations are running when resizing the queue
	// this sync point is not hit during normal execution and only during movie playback
	// and even there it doesn't add wait time
	FinishAnimationComputations();
#endif
	uint32 numAnimsInLayer=m_layers[nLayer].m_transitionQueue.m_animations.size();
	for (size_t i=0; i<numAnimsInLayer; i++)
		UnloadAnimationAssets(m_layers[nLayer].m_transitionQueue.m_animations,i);

	m_layers[nLayer].m_transitionQueue.m_animations.resize(0);

	//Make sure to clear the active flag for the layer
	SetActiveLayer(nLayer, 0);
}


void CSkeletonAnim::UnloadAnimationAssets(DynArray<CAnimation>& arrAFIFO, int num)
{
	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;

	CAnimation& rAnim = arrAFIFO[num];

	if (rAnim.m_Parametric == NULL)
	{
		int32 nAnimID = rAnim.m_nAnimID;
		const ModelAnimationHeader* pAnim = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(nAnimID) );
		if (pAnim->m_nAssetType==CAF_File)
		{
#ifdef _DEBUG
			const char* pname = pAnim->GetAnimName();
#endif
			int32 nGlobalID = pAnimationSet->GetGlobalIDByAnimID_Fast(nAnimID);
			GlobalAnimationHeaderCAF& rGAH = g_AnimationManager.m_arrGlobalCAF[ nGlobalID ];

			if (rGAH.m_nRef_at_Runtime)
			{
				rGAH.m_nRef_at_Runtime--;
			}
		}
	}
	else
	{

		uint32 numAnims0 = rAnim.m_Parametric->m_numAnims;
		for (uint32 i=0; i<numAnims0; i++)
		{
			int32 nAnimID = rAnim.m_Parametric->m_nAnimID[i];
			const ModelAnimationHeader* pAnim = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(nAnimID) );
			if (pAnim->m_nAssetType==CAF_File)
			{
	#ifdef _DEBUG
				const char* pname = pAnim->GetAnimName();
	#endif
				int32 nGlobalID = pAnimationSet->GetGlobalIDByAnimID_Fast(nAnimID);
				GlobalAnimationHeaderCAF& rGAH = g_AnimationManager.m_arrGlobalCAF[ nGlobalID ];

				if (rGAH.m_nRef_at_Runtime)
				{
					rGAH.m_nRef_at_Runtime--;

					/*
					const char* mname = rGAH.GetFilePath();
					uint32 name0 = strcmp(mname,"animations/human/male/smartobjects/cover/rifle/coverlow_tac_idle_rifle_rgt_01.caf");
					uint32 name1 = strcmp(mname,"animations/human/male/smartobjects/cover/rifle/coverlow_tac_idle_rifle_lft_01.caf");
					if ( name0==0 || name1==0 )
					{
					int nCurrentFrameID = g_pCharacterManager->m_nUpdateCounter; 
					g_pISystem->Warning( VALIDATOR_MODULE_ANIMATION,VALIDATOR_WARNING, VALIDATOR_FLAG_FILE,0,	"Dec1: Frame:%d  %x Name: %s",nCurrentFrameID, rGAH.m_nRef_at_Runtime,rGAH.m_FilePath );
					} */

				}

				/*if (rGAH.IsAssetOnDemand())
				{
					if (rGAH.m_nRef_at_Runtime==0)
					{
						//g_AnimationManager.UnloadAnimationCAF(nGlobalID);
						//g_pISystem->Warning( VALIDATOR_MODULE_ANIMATION,VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, 0 , "Unload asset: %s" ,rGAH.m_FilePath.c_str() );
					}
				}*/
			}
		}

		LMG::FreeRuntimeParametric(rAnim.m_Parametric);
		rAnim.m_Parametric = NULL;
	}
}

CAnimation* CSkeletonAnim::FindAnimInFIFO( uint32 nUserToken,int nLayer, bool includeDeferredAnimation )
{
	int startLayer = 0;
	int endLayer = numVIRTUALLAYERS;
	if (nLayer >= 0)
	{
		assert( nLayer >= 0 && nLayer < numVIRTUALLAYERS );
		startLayer = nLayer;
		endLayer = nLayer+1;
	}

	for (int lyr = startLayer; lyr < endLayer; lyr++)
	{
		DynArray<CAnimation>& fifo = m_layers[lyr].m_transitionQueue.m_animations;
		for (int i = 0,num = (int)fifo.size(); i < num; i++)
		{
			if (fifo[i].m_AnimParams.m_nUserToken == nUserToken)
			{
				return &fifo[i];
			}
		}
	}

	if (includeDeferredAnimation)
	{
		for (int i = 0,num = (int)m_arrDeferredQueueUpdates.size(); i < num; i++)
		{
			if (m_arrDeferredQueueUpdates[i].m_nLayer >= startLayer && m_arrDeferredQueueUpdates[i].m_nLayer < endLayer)
			{
				if (m_arrDeferredQueueUpdates[i].m_Anim.m_AnimParams.m_nUserToken == nUserToken)
				{
					return &m_arrDeferredQueueUpdates[i].m_Anim;
				}
			}
		}
	}
	return NULL;
}

void CSkeletonAnim::ManualSeekAnimationInFIFO(uint32 nLayer, uint32 num, float time2, bool triggerAnimEvents)
{
	CAnimationSet* pAnimationSet = &m_pInstance->m_pModel->m_AnimationSet;
	uint32 numAnimsLayer = GetNumAnimsInFIFO(nLayer);
	if (num < numAnimsLayer)
	{
		CAnimation &animation = GetAnimFromFIFO(nLayer, num);

		if (animation.m_AnimParams.m_nFlags & (CA_MANUAL_UPDATE | CA_TRACK_VIEW_EXCLUSIVE))
		{

			if (animation.m_Parametric == NULL)
			{
				//this is a CAF file

				int32 animID		=  animation.m_nAnimID;
				assert(animID>=0);
				uint32 globalID	= pAnimationSet->GetGlobalIDByAnimID_Fast(animID);
				assert(globalID>=0);

				uint32 numCAFs = g_AnimationManager.m_arrGlobalCAF.size();
				assert(globalID<numCAFs);

				if ((globalID>=0) && (globalID<(int32)numCAFs))
				{
					const ModelAnimationHeader* pAnim = SPU_MAIN_PTR( pAnimationSet->GetModelAnimationHeader(animID) );
					if (pAnim->m_nAssetType==CAF_File)
					{
						const GlobalAnimationHeaderCAF& rGlobalAnimHeader = g_AnimationManager.m_arrGlobalCAF[globalID];

						f32 timeold, timenew;
						timeold = animation.m_fAnimTime, timenew = time2;

						// prevent sending events when animation is not playing
						// otherwise it may happen to get the event each frame
						bool bProgress = timeold < timenew;
						if ( bProgress && triggerAnimEvents)
						{
							uint32 numEvents = rGlobalAnimHeader.m_AnimEventsCAF.size();
							for (uint32 i=0; i<numEvents; i++)
							{
								f32 time = rGlobalAnimHeader.m_AnimEventsCAF[i].m_time;
								if ((timeold<=time && timenew>=time) || (timeold<=time + 1.0f && timenew>=time + 1.0f))
								{
									m_LastAnimEvent.m_time=time; 
									m_LastAnimEvent.m_AnimPathName=	rGlobalAnimHeader.GetFilePath(); 
									m_LastAnimEvent.m_AnimID = animID;
									m_LastAnimEvent.m_EventName				=	rGlobalAnimHeader.m_AnimEventsCAF[i].m_strEventName.c_str();
									m_LastAnimEvent.m_EventNameLowercaseCRC32 =	rGlobalAnimHeader.m_AnimEventsCAF[i].m_EventNameLowercaseCRC32;
									m_LastAnimEvent.m_CustomParameter	=	rGlobalAnimHeader.m_AnimEventsCAF[i].m_strCustomParameter;
									m_LastAnimEvent.m_BonePathName		= rGlobalAnimHeader.m_AnimEventsCAF[i].m_strBoneName.c_str();
									m_LastAnimEvent.m_vOffset					= rGlobalAnimHeader.m_AnimEventsCAF[i].m_vOffset;
									m_LastAnimEvent.m_vDir						= rGlobalAnimHeader.m_AnimEventsCAF[i].m_vDir;

									if (m_pEventCallback)		
										(*m_pEventCallback)(m_pInstance,m_pEventCallbackData);
								}			
							}
						}
					}
				}
			}
			else 
			{
				const ModelAnimationHeader* pAnim = pAnimationSet->GetModelAnimationHeader(animation.m_nAnimID);
				assert(pAnim->m_nAssetType==LMG_File);

				int32 globalID	= pAnim->m_nGlobalAnimId;
				assert(globalID >= 0 && globalID < int(g_AnimationManager.m_arrGlobalLMG.size()));
				if (globalID >= 0 && globalID < int(g_AnimationManager.m_arrGlobalLMG.size()))
				{
					const GlobalAnimationHeaderLMG& rGlobalAnimHeaderLMG = g_AnimationManager.m_arrGlobalLMG[globalID];

					f32 timeold, timenew;
					timeold = animation.m_fAnimTime, timenew = time2;

					// prevent sending events when animation is not playing
					// otherwise it may happen to get the event each frame
					bool bProgress = timeold < timenew;
					if ( bProgress && triggerAnimEvents)
					{
						uint32 numEvents = rGlobalAnimHeaderLMG.m_AnimEventsLMG.size();
						for (uint32 i=0; i<numEvents; i++)
						{
							f32 time = rGlobalAnimHeaderLMG.m_AnimEventsLMG[i].m_time;
							if ((timeold<=time && timenew>=time) || (timeold<=time + 1.0f && timenew>=time + 1.0f))
							{
								m_LastAnimEvent.m_time=time; 
								m_LastAnimEvent.m_AnimPathName		=	rGlobalAnimHeaderLMG.GetFilePath(); 
								m_LastAnimEvent.m_AnimID					= animation.m_nAnimID;
								m_LastAnimEvent.m_EventName				=	rGlobalAnimHeaderLMG.m_AnimEventsLMG[i].m_strEventName.c_str(); 
								m_LastAnimEvent.m_EventNameLowercaseCRC32 = rGlobalAnimHeaderLMG.m_AnimEventsLMG[i].m_EventNameLowercaseCRC32; 
								m_LastAnimEvent.m_CustomParameter	=	rGlobalAnimHeaderLMG.m_AnimEventsLMG[i].m_strCustomParameter;
								m_LastAnimEvent.m_BonePathName		= rGlobalAnimHeaderLMG.m_AnimEventsLMG[i].m_strBoneName.c_str();
								m_LastAnimEvent.m_vOffset					= rGlobalAnimHeaderLMG.m_AnimEventsLMG[i].m_vOffset;
								m_LastAnimEvent.m_vDir						= rGlobalAnimHeaderLMG.m_AnimEventsLMG[i].m_vDir;

								if (m_pEventCallback)		
									(*m_pEventCallback)(m_pInstance,m_pEventCallbackData);
							}			
						}
					}
				}
			}




			// Update the time.
			animation.m_fAnimTime = time2;
		}
	}
}
